/*
 * DbConnection.h
 *
 *  Created on: 2018年1月3日
 *      Author: chuanjiang.zh@qq.com
 */

#ifndef DBCONNECTION_H_
#define DBCONNECTION_H_

#include "DbType.h"
#include "DbRow.h"
#include <functional>
#include "CLog.h"


template <class T>
bool fromRow(T& t, db::DbRow& row)
{
    CLog::error("unknown fromRow. %s\n", typeid(t).name());
    return false;
}

typedef std::function< bool(db::DbRow& row) >   RowMapper;


namespace db
{


class DbConnectionPool;

class DbConnection
{
public:
    virtual ~DbConnection();

    explicit DbConnection(DbConnectionPool* pool);
    explicit DbConnection(DbConnectionPool& pool);
    explicit DbConnection(soci::session& session);

    bool validate();

	bool isCrashed();

    /**
     * 插入并返回自增长ID
     * @param sql
     * @param table	表名称
     * @param id
     * @return
     */
    bool insert(const char* sql, const char* table, int64_t& id);

    /**
     * 执行sql, 返回记录变更数
     * @param sql
     * @return
     */
    int update(const char* sql);

    bool execute(const char* sql);

    bool query(const char* sql, RowMapper mapper);

    int queryRows(const char* sql, RowMapper mapper);


    int update(const std::string& sql)
    {
        return update(sql.c_str());
    }

    bool execute(const std::string& sql)
    {
        return execute(sql.c_str());
    }

    template <class T>
	bool queryValue(const char* sql, T& value)
    {
        RowMapper mapper = [&] (DbRow& r)
        {
            return r.getValue((size_t)0, value);
        };
        return query(sql, mapper);
    }

    template <class T>
    bool queryValue(const std::string& sql, T& value)
    {
        RowMapper mapper = [&] (DbRow& r)
        {
            return r.getValue((size_t)0, value);
        };
        return query(sql.c_str(), mapper);
    }

    bool queryTime(const char* sql, time_t& value);

    bool queryTime(const std::string& sql, time_t& value)
    {
        return queryTime(sql.c_str(), value);
    }


    template < class Cont >
    int queryValues(const char* sql, Cont& cont)
    {
        int count = 0;
        RowMapper mapper = [&] (DbRow& row)
        {
            size_t pos = 0;
            typename Cont::value_type value = typename Cont::value_type();
            if (row.get(pos, value))
            {
                cont.push_back(value);
                count ++;
            }

            return true;
        };
        return queryRows(sql, mapper);
    }





    soci::session& getSession();

    bool reconnect();

    DbSourceType getSourceType();
    bool isSqlite();
    bool isMysql();


    template < class T >
    bool queryObject(const char* sql, T& obj)
    {
        RowMapper mapper = [&] (DbRow& row)
        {
            return fromRow(obj, row);
        };

        return query(sql, mapper);
    }


    template < class Cont >
    int queryObjects(const char* sql, Cont& cont)
    {
        RowMapper mapper = [&] (DbRow& row)
        {
            typename Cont::value_type value = typename Cont::value_type();
            if (fromRow(value, row))
            {
                cont.push_back(value);
            }

            return true;
        };

        return queryRows(sql, mapper);
    }

    template < class T >
    int queryObjects(const char* sql, T* objs, int maxSize)
    {
        int count = 0;
        RowMapper mapper = [&] (DbRow& row)
        {
            if (count >= maxSize)
            {
                return false;
            }

            if (fromRow(objs[count], row))
            {
                count ++;
            }

            return count < maxSize;
        };

        return queryRows(sql, mapper);
    }



    template < class Cont >
    int queryValues(std::string& sql, Cont& cont)
    {
        return queryValues(sql.c_str(), cont);
    }

    template < class T >
    bool queryObject(const std::string& sql, T& obj)
    {
        return queryObject(sql.c_str(), obj);
    }

    template < class Cont >
    int queryObjects(const std::string& sql, Cont& cont)
    {
        return queryObjects(sql.c_str(), cont);
    }


protected:
    bool doInsert(const char* sql, const char* table, int64_t& id, bool& gotId);
    bool doExecute(const char* sql);

	void reopen();

protected:
    DbConnectionPool*   m_pool;
    soci::session*  m_owner;
    soci::session*  m_session;

};


} /* namespace db */

#endif /* DBCONNECTION_H_ */
